package org.zztl.json.parser;

import org.zztl.json.JSONException;

import java.util.*;

public class JSONParser {

    private Lexer lexer;

    public JSONParser(String input) {
        this.lexer = new Lexer(input);
    }


    public Object parse() {
        switch (lexer.nextToken().getType()) {
            case Token.LBRACE:
                Map<String, Object> object = new LinkedHashMap<String, Object>();
                parseObject(object);
                return object;
            case Token.LBRACKET:
                List<Object> array = new LinkedList<Object>();
                parseArray(array);
                return array;
            default:
                throw new JSONException("syntax error");
        }
    }

    private void parseArray(List<Object> array) {
        Token token;
        while ((token = lexer.nextToken()).getType() != Token.EOF) {
            while (token.getType() == Token.COMMA) {
                token=lexer.nextToken();
            }
            switch (token.getType()) {
                case Token.LITERAL_INT:
                    array.add(Integer.valueOf(token.getText()));
                    break;
                case Token.LITERAL_FLOAT:
                    array.add(Double.valueOf(token.getText()));
                    break;
                case Token.LITERAL_STRING:
                    array.add(token.getText());
                    break;
                case Token.TRUE:
                    array.add(Boolean.valueOf(token.getText()));
                    break;
                case Token.FALSE:
                    array.add(Boolean.valueOf(token.getText()));
                    break;
                case Token.NULL:
                    array.add(null);
                    break;
                case Token.LBRACKET:
                    List<Object> subArray = new LinkedList<Object>();
                    array.add(subArray);
                    parseArray(subArray);
                    break;
                case Token.RBRACKET:
                    return;
                case Token.LBRACE:
                    Map<String, Object> subObject = new LinkedHashMap<String, Object>();
                    array.add(subObject);
                    parseObject(subObject);
                    break;
                case Token.RBRACE:
                    return;
                default:
                    throw new JSONException("parse jsonArray error");
            }
        }
        throw new JSONException("syntax error");
    }

    private void parseObject(Map<String, Object> object) {
        Token token;
        while ((token = lexer.nextToken()).getType() != Token.EOF) {
            while (token.getType() == Token.COMMA) {
                token=lexer.nextToken();
            }

            if(token.getType()==Token.RBRACE){
                return;
            }

            if(token.getType()!=Token.LITERAL_STRING){
                throw new JSONException("syntax error");
            }
            String key = token.getText();

            token=lexer.nextToken();

            if(token.getType()!=Token.COLON){
                throw new JSONException("syntax error");
            }
            token=lexer.nextToken();

            switch (token.getType()) {
                case Token.LITERAL_INT:
                    object.put(key,Integer.valueOf(token.getText()));
                    break;
                case Token.LITERAL_FLOAT:
                    object.put(key,Double.valueOf(token.getText()));
                    break;
                case Token.LITERAL_STRING:
                    object.put(key,token.getText());
                    break;
                case Token.TRUE:
                    object.put(key,Boolean.valueOf(token.getText()));
                    break;
                case Token.FALSE:
                    object.put(key,Boolean.valueOf(token.getText()));
                    break;
                case Token.NULL:
                    object.put(key,null);
                    break;
                case Token.LBRACKET:
                    List<Object> subArray = new LinkedList<Object>();
                    object.put(key,subArray);
                    parseArray(subArray);
                    break;
                case Token.RBRACKET:
                    return;
                case Token.LBRACE:
                    Map<String, Object> subObject = new LinkedHashMap<String, Object>();
                    object.put(key,subObject);
                    parseObject(subObject);
                    break;
                case Token.RBRACE:
                    return;
                default:
                    throw new JSONException("parse jsonArray error");
            }
        }
        throw new JSONException("syntax error");
    }

}
